#pragma once

using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Diagnostics;
using namespace System::Windows::Forms;
using namespace System::Drawing;
using namespace System::Collections::Generic;

#include "BridgeWrapper.h"
#include "Wire.h"

ref class Sheet : public UserControl
{
	ref struct State abstract
	{
	};

	ref struct StateBeginningDrag : State
	{
		MouseEventArgs^ mouseDownEvent;

		literal int Threshold = 7;

		StateBeginningDrag (MouseEventArgs^ mouseDownEvent);
	};

	ref struct StateDraggingBridge : State
	{
		Bridge^ bridge;
		float lastMouseX;
		float lastMouseY;
		StateDraggingBridge (Bridge^ bridge, float lastMouseX, float lastMouseY);
	};

	ref struct StateDrawingNewWire : State
	{
		Bridge^ startBridge;
		int startPortIndex;

		// When the snapping port is the same as the starting port, it means the wire being drawn
		// is zero-length; in this case we won't add it to the list of wires when the mouse is released.
		Bridge^ snappingBridge;
		int snappingPortIndex;

		// These values only have meaning when snappingBridge is not valid
		float endX;
		float endY;

		StateDrawingNewWire (Bridge^ startBridge, int startPortIndex);
	};

	ref struct StateMovingWirePoint : State
	{
		Wire^ wire;
		int pointIndex;

		Bridge^ snappingBridge;
		int snappingPortIndex;

		// These values only have meaning when snappingBridge is not valid
		float x;
		float y;

		StateMovingWirePoint (Wire^ wire, int pointIndex, float mouseX, float mouseY);
	};

	ref struct StateMovingInteriorPort : State
	{
		Bridge^ bridge;
		int portIndex;

		StateMovingInteriorPort (Bridge^ bridge, int portIndex);
	};

	ref struct StateResizingBridge : State
	{
		enum class Corner
		{
			UpperLeft,
			UpperRight,
			LowerLeft,
			LowerRight
		};

		float offsetX;
		float offsetY;

		float lastMouseX;
		float lastMouseY;

		Bridge^ bridge;
		Corner corner;

		StateResizingBridge (Bridge^ bridge, Corner corner, float mouseX, float mouseY);
	};

	State^ state;

	static List<Wire^>^ wires = gcnew List<Wire^> ();

	static Brush^ wiredPortBrush = gcnew SolidBrush (Color::Blue);
	
	Bridge^ selectedBridge;
	int selectedPortIndex;

	TextBox^ logTextBox;
	ComboBox^ logComboBoxPort;
	ComboBox^ logComboBoxTree;
	PropertyGrid^ bridgeOrPortPropertyGrid;
	PropertyGrid^ treePropertyGrid;

private: System::Windows::Forms::ContextMenuStrip^  contextMenuStripBridge;
private: System::Windows::Forms::ToolStripMenuItem^  powerUpToolStripMenuItem;
private: System::Windows::Forms::Timer^  timer1;
private: System::Windows::Forms::ToolStripMenuItem^  powerDownToolStripMenuItem;

public:
	Sheet (TextBox^ logTextBox,
		ComboBox^ logComboBoxPort,
		ComboBox^ logComboBoxTree,
		PropertyGrid^ bridgeOrPortPropertyGrid,
		PropertyGrid^ treePropertyGrid);

	Sheet(System::ComponentModel::IContainer ^container)
	{
		/// <summary>
		/// Required for Windows.Forms Class Composition Designer support
		/// </summary>

		container->Add(this);
		InitializeComponent();
	}

protected:
	/// <summary>
	/// Clean up any resources being used.
	/// </summary>
	~Sheet();

	virtual void OnPaint (PaintEventArgs^ e) override;
	virtual void OnMouseDown  (MouseEventArgs^ e) override;
	virtual void OnMouseMove  (MouseEventArgs^ e) override;
	virtual void OnMouseUp    (MouseEventArgs^ e) override;
	virtual void OnMouseClick (MouseEventArgs^ e) override;
	virtual property System::Windows::Forms::CreateParams^ CreateParams { System::Windows::Forms::CreateParams^ get () override; }

private:
	/// <summary>
	/// Required designer variable.
	/// </summary>
	System::ComponentModel::IContainer^  components;

#pragma region Windows Form Designer generated code
	/// <summary>
	/// Required method for Designer support - do not modify
	/// the contents of this method with the code editor.
	/// </summary>
	void InitializeComponent(void)
	{
		this->components = (gcnew System::ComponentModel::Container());
		this->contextMenuStripBridge = (gcnew System::Windows::Forms::ContextMenuStrip(this->components));
		this->powerUpToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem());
		this->powerDownToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem());
		this->timer1 = (gcnew System::Windows::Forms::Timer(this->components));
		this->contextMenuStripBridge->SuspendLayout();
		this->SuspendLayout();
		// 
		// contextMenuStripBridge
		// 
		this->contextMenuStripBridge->Items->AddRange(gcnew cli::array< System::Windows::Forms::ToolStripItem^  >(2) {this->powerUpToolStripMenuItem, 
			this->powerDownToolStripMenuItem});
		this->contextMenuStripBridge->Name = L"contextMenuStripBridge";
		this->contextMenuStripBridge->Size = System::Drawing::Size(163, 52);
		this->contextMenuStripBridge->Opening += gcnew System::ComponentModel::CancelEventHandler(this, &Sheet::contextMenuStripBridge_Opening);
		// 
		// powerUpToolStripMenuItem
		// 
		this->powerUpToolStripMenuItem->Name = L"powerUpToolStripMenuItem";
		this->powerUpToolStripMenuItem->Size = System::Drawing::Size(162, 24);
		this->powerUpToolStripMenuItem->Text = L"Power Up";
		this->powerUpToolStripMenuItem->Click += gcnew System::EventHandler(this, &Sheet::powerUpToolStripMenuItem_Click);
		// 
		// powerDownToolStripMenuItem
		// 
		this->powerDownToolStripMenuItem->Name = L"powerDownToolStripMenuItem";
		this->powerDownToolStripMenuItem->Size = System::Drawing::Size(162, 24);
		this->powerDownToolStripMenuItem->Text = L"Power Down";
		this->powerDownToolStripMenuItem->Click += gcnew System::EventHandler(this, &Sheet::powerDownToolStripMenuItem_Click);
		// 
		// timer1
		// 
		this->timer1->Enabled = true;
		this->timer1->Interval = 1000;
		this->timer1->Tick += gcnew System::EventHandler(this, &Sheet::timer1_Tick);
		// 
		// Sheet
		// 
		this->BackColor = System::Drawing::SystemColors::Window;
		this->Name = L"Sheet";
		this->Size = System::Drawing::Size(384, 313);
		this->contextMenuStripBridge->ResumeLayout(false);
		this->ResumeLayout(false);

	}
#pragma endregion

private:
	void PaintLegend (Graphics^ g);
	Bridge^ GetBridgeAtCoords (float x, float y);
	int GetInteriorPortAtCoords (Bridge^ bridge, float x, float y);
	bool GetExteriorPortAtCoords (float x, float y, float rectangleSize, Bridge^% bridgeOut, int% portIndexOut);
	bool IsPortWired (Bridge^ bridge, int portIndex);

	// called by the framework when the used changes the combo box
	void OnFilterComboBoxChanged (Object^ sender, EventArgs^ e);

	// Callbacks for the Bridge objects to call
	void OnDebugStrOut (Bridge^ bridge, int portIndex, int treeIndex, String^ str);
	void OnBpduTransmitCallback (Bridge^ bridge, int portIndex, const unsigned char* bpdu, unsigned int bpduSize);

public:
	void AddNewBridge (int portCount);
	void StartAll ();
	void StopAll ();
	void PauseAll ();
	void ResumeAll ();
	void SaveToFile (String^ filename);
	void LoadFromFile (String^ filename);

private:
	void contextMenuStripBridge_Opening (Object^ sender, CancelEventArgs^ e);
	void powerUpToolStripMenuItem_Click (Object^ sender, EventArgs^ e);
	void powerDownToolStripMenuItem_Click (Object^ sender, EventArgs^ e);
	void timer1_Tick (Object^ sender, EventArgs^ e);
	void OnMouseUp_StateMovingWirePoint (MouseEventArgs^ e);
	void OnMouseUp_StateDrawingNewWire (MouseEventArgs^ e);
	void OnMouseMove_StateBeginningDrag (MouseEventArgs^ e);
	void OnMouseMove_StateDraggingBridge (MouseEventArgs^ e);
	void OnMouseMove_StateDrawingNewWire (MouseEventArgs^ e);
	void OnMouseMove_StateMovingWirePoint (MouseEventArgs^ e);
	void OnMouseMove_StateMovingInteriorPort (MouseEventArgs^ e);
	void OnMouseMove_StateResizingBridge (MouseEventArgs^ e);
};
